package cn.project.domain.strategy.service.armory;

import cn.project.domain.strategy.model.entity.StrategyAwardEntity;
import cn.project.domain.strategy.model.entity.StrategyEntity;
import cn.project.domain.strategy.model.entity.StrategyRuleEntity;
import cn.project.domain.strategy.repository.IStrategyRepository;
import cn.project.types.common.Constants;
import cn.project.types.enums.ResponseCode;
import cn.project.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.security.SecureRandom;
import java.util.*;

/**
 * @author xu
 * @description 抽奖策略算法抽象类
 * @date 2025/8/12
 */
@Slf4j
public abstract class AbstractStrategyAlgorithm implements IStrategyArmory, IStrategyDispatch{
    @Resource
    protected IStrategyRepository strategyRepository;

    @Override
    public boolean assembleLotteryStrategy(Long strategyId) {
        log.info("1.查询策略配置");
        List<StrategyAwardEntity> strategyAwardEntities = strategyRepository.queryStrategyAwardList(strategyId);
        log.info("2.缓存奖品库存, 用于decr扣减库存使用");
        for(StrategyAwardEntity strategyAward: strategyAwardEntities){
            Integer awardId = strategyAward.getAwardId();
            Integer awardCount = strategyAward.getAwardCount();
            cacheStrategyAwardCount(strategyId,awardId,awardCount);
        }

        assembleLotteryStrategy(String.valueOf(strategyId),strategyAwardEntities);
        log.info("3-1.权重策略配置-适用于 rule_weight 权重规则配置");
        StrategyEntity strategyEntity = strategyRepository.queryStrategyEntityByStrategyId(strategyId);
        log.info("3-2.获取ruleModel 中的 rule_weight");
        String ruleWeight = strategyEntity.getRuleModels();
        log.info("3-3.获取ruleModel 中的 rule_weight{}",ruleWeight);
        if(ruleWeight == null) return true;
        StrategyRuleEntity strategyRuleEntity = strategyRepository.queryStrategyRule(strategyId,ruleWeight);
        log.info("3-4.获取ruleModel 中的 strategyRuleEntity{}",strategyRuleEntity);
        if(strategyRuleEntity == null) throw new AppException(ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getCode(),ResponseCode.STRATEGY_RULE_WEIGHT_IS_NULL.getInfo());
        /**
         * 遍历不同权重分支生成子策略
         * {
         *   "rule1": [101, 102],
         *   "rule2": [103, 104, 105]
         * }
         */
        log.info("4.將ruleModel 中的 rule_weight装配");
        Map<String, List<Integer>> ruleWeightValuesMap = strategyRuleEntity.getRuleWeightValues();
        Set<String> keys = ruleWeightValuesMap.keySet();
        for (String key : keys) {
            List<Integer> ruleWeightValues = ruleWeightValuesMap.get(key);
            ArrayList<StrategyAwardEntity> strategyAwardEntitiesClone = new ArrayList<>(strategyAwardEntities);
            strategyAwardEntitiesClone.removeIf(entity->!ruleWeightValues.contains(entity.getAwardId()));
            assembleLotteryStrategy(String.valueOf(strategyId).concat("_").concat(key),strategyAwardEntitiesClone);
        }
        return true;
    }

    @Override
    public boolean assembleLotteryStrategyByActivityId(Long activityId) {
        Long strategyId = strategyRepository.queryStrategyIdByActivityId(activityId);
        return assembleLotteryStrategy(strategyId);
    }

    private void cacheStrategyAwardCount(Long strategyId, Integer awardId, Integer awardCount) {
        log.info("缓存奖品库存, 用于decr扣减库存使用");
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
        strategyRepository.cacheStrategyAwardCount(cacheKey, awardCount);
    }


    private void assembleLotteryStrategy(String key, List<StrategyAwardEntity> strategyAwardEntities) {

        log.info("1.获取最小概率值");
        BigDecimal minAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
        log.info("2.获取概率值的总和");
        BigDecimal totalAwardRate = strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .reduce(BigDecimal.ZERO,BigDecimal::add);

        log.info("3.获取概率值比例,用1%0.0001获取概率范围，百分位，千分位");
        BigDecimal rateRange = totalAwardRate.divide(minAwardRate, 0, RoundingMode.CEILING);

        log.info("4.策略");
        ArrayList<Integer> strategyAwardSearchRateTables = new ArrayList<>(rateRange.intValue());
        for(StrategyAwardEntity strategyAwardEntity : strategyAwardEntities){
            Integer awardId = strategyAwardEntity.getAwardId();
            BigDecimal awardRate = strategyAwardEntity.getAwardRate();
            // 计算出每个概率值需要查找表的数量，循环填充
            for (int i = 0; i < rateRange.multiply(awardRate).setScale(0, RoundingMode.CEILING).intValue(); i++){
                strategyAwardSearchRateTables.add(awardId);
            }
        }
        log.info("5.乱序");
        Collections.shuffle(strategyAwardSearchRateTables);
        log.info("6.乱序entity列表");
        HashMap<Integer,Integer> shuffleStrategyAwardSearchRateTables = new HashMap<>();
        for (int i = 0; i < strategyAwardSearchRateTables.size(); i++){
            shuffleStrategyAwardSearchRateTables.put(i, strategyAwardSearchRateTables.get(i));
        }
        log.info("7.存储到redis");
        strategyRepository.storeStrategyAwardSearchRateTables(key,shuffleStrategyAwardSearchRateTables.size(),shuffleStrategyAwardSearchRateTables);
    }


    @Override
    public Integer getRandomAwardId(Long strategyId) {
        // 生成范围
        int rateRange = strategyRepository.getRateRange(strategyId);
        // 通过随机值查找奖品信息
        return strategyRepository.getStrategyAwardAssemble(String.valueOf(strategyId),new SecureRandom().nextInt(rateRange));
    }

    @Override
    public Integer getRandomAwardId(Long strategyId,String ruleWeightValue) {
        String key = String.valueOf(strategyId).concat("_").concat(ruleWeightValue);
        // 生成范围
        int rateRange = strategyRepository.getRateRange(key);
        // 通过随机值查找奖品信息
        return strategyRepository.getStrategyAwardAssemble(key,new SecureRandom().nextInt(rateRange));
    }

    @Override
    public Boolean subtractionAwardStock(Long strategyId, Integer awardId) {
        String cacheKey = Constants.RedisKey.STRATEGY_AWARD_COUNT_KEY + strategyId + Constants.UNDERLINE + awardId;
        return strategyRepository.subtractionAwardStock(cacheKey);
    }

    // 概率最小值
    protected BigDecimal minAwardRate(List<StrategyAwardEntity> strategyAwardEntities){
        return strategyAwardEntities.stream()
                .map(StrategyAwardEntity::getAwardRate)
                .min(BigDecimal::compareTo)
                .orElse(BigDecimal.ZERO);
    }

    // 概率范围值，百分位、千分位、万分位
    protected double convert(double min) {
        if (0 == min) return 1D;

        String minStr = String.valueOf(min);

        // 小数点前
        String beginVale = minStr.substring(0, minStr.indexOf("."));
        int beginLength = 0;
        if (Double.parseDouble(beginVale) > 0) {
            beginLength = minStr.substring(0, minStr.indexOf(".")).length();
        }

        // 小数点后
        String endValue = minStr.substring(minStr.indexOf(".") + 1);
        int endLength = 0;
        if (Double.parseDouble(endValue) > 0) {
            endLength = minStr.substring(minStr.indexOf(".") + 1).length();
        }

        return Math.pow(10, beginLength + endLength);
    }

    /**
     * 装配算法
     * @param key 为策略ID、权重ID
     * @param strategyAwardEntities 对应的奖品概率
     */
    protected abstract void armoryAlgorithm(String key, List<StrategyAwardEntity> strategyAwardEntities);

    protected abstract Integer dispatchAlgorithm(String key);
}
